//
//  main.swift
//  Swift闭包Closure
//
//  Created by durban.zhang on 2018/5/8.
//  Copyright © 2018年 durban.zhang. All rights reserved.
//

import Foundation

print("5、Swift闭包Closure")

let names = ["a", "b", "d", "c", "e"]
func backward(_ s1:String, _ s2:String) -> Bool {
    return s1 > s2
}

var reversedNames = names.sorted()
print("The default sorted reversedNames is \(reversedNames)")
reversedNames = names.sorted(by: backward)
print("The custom sorted reversedNames is \(reversedNames)")

/*
 闭包表达式语法
 
 {(parameters) -> return type in
 
     statements
 
 }
 
 将上面的函数用闭包的方式来实现下
 */
reversedNames = names.sorted(by: {(s1: String, s2:String) -> Bool in
    return s1 < s2
})
print("The Closure custom sorted reversedNames is \(reversedNames)")

// 这里闭包的逻辑是比较短的，我们可以写成一行，如下
reversedNames = names.sorted(by: {(s1:String, s2:String) -> Bool in return s1 > s2 })
print("The New Closure custom sorted reversedNames is \(reversedNames)")

// 下面是几种简洁的用法
reversedNames = names.sorted(by: {s1, s2 in
    return s1 > s2
})

// 单个表达式闭包的隐式返回 - 不用写 return
reversedNames = names.sorted(by: {s1, s2 in s1 > s2})

// 速记参数名
reversedNames = names.sorted(by: { $0 > $1 })

// 操作符方法
reversedNames = names.sorted(by: >)

/*
 尾随闭包
 
 如果你想要传递一个闭包表达式到一个函数中，作为最后一个参数同时这个闭包表达式很长时，使用“尾随闭包”是很有用的
 */
func someFunctionThatTakesAClosure(closure: () -> Void) {
    // some logical
}

// 一般情况下的调用方式
someFunctionThatTakesAClosure(closure: {
    // 闭包的整体在这里
})

// 这里的调用方式就是尾随闭包的调用方式
someFunctionThatTakesAClosure {
    // 尾随闭包的整体在这里
}

// 对上面的字符串使用尾随闭包的方式做下修改
reversedNames = names.sorted() { $0 > $1 }

/*
 这里的函数sorted函数只有一个参数，那么我们在通过尾随闭包的方式使用的时候，还可以使用下面的方式使用，省略圆括号
 */
reversedNames = names.sorted { $0 > $1 }

// 我们再举一个例子，比如Array的方法map(_:)，看下是如何使用尾随闭包的
let digitNames = [
    0: "Zero",
    1: "One",
    2: "Two",
    3: "Three",
    4: "Four",
    5: "Five",
    6: "Six",
    7: "Seven",
    8: "Eight",
    9: "Nine",
]

let numbers = [16, 58, 510]
let strings = numbers.map {(number) -> String
    in
    var number = number
    var output = ""
    repeat {
        print("number % 10 = \(number % 10)")
        output = digitNames[number % 10]! + output
        number /= 10
    } while number > 0
    
    return output
}

print("strings = \(strings)")

// 捕获数值
func makeIncremeter(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementer() -> Int {
        runningTotal += amount
        return runningTotal
    }
    
    return incrementer
}

/*
 上面的返回值类型是() -> Int，代表返回来的是一个函数，这个函数是没有参数的，但是执行这个函数后会返回一个Int类型的值
 makeIncremeter(forIncrement:)函数体内定义了一个整型变量runningTotal，初始值为0，用来在incrementer里面执行时存储值
 makeIncremeter(forIncrement:)函数有一个参数，标签是forIncrement，参数名为amount
 makeIncremeter(forIncrement:)函数体内的嵌套函数incrementer，每次执行的时候，会将amount的值加到runningTotal，然后返回
 */

/*
 如果将
 func incrementer() -> Int {
     runningTotal += amount
     return runningTotal
 }
 作为一个单独的个体拿出来的话，是没有意义的，因为runningTotal和amount，并没有具体的参数传过来。他只是从外部的捕获了runningTotal和amount的值，然后在自己内部使用
 
 */
let incrementTen = makeIncremeter(forIncrement: 10)
print("First Ten is \(incrementTen())")
print("Second Ten is \(incrementTen())")
print("Third Ten is \(incrementTen())")


let incrementSeven = makeIncremeter(forIncrement: 7)
print("First Seven is \(incrementSeven())")
print("Second Seven is \(incrementSeven())")
print("Third Seven is \(incrementSeven())")

/*
 从incrementTen和incrementSeven可以看出，runningTotal是一个独立的变量，在他们之间是互不影响
 */
print("Four Ten is \(incrementTen())")

// 闭包是参考类型,如下
let alsoIncrementTen = incrementTen
print("alsoIncrementTen is \(alsoIncrementTen())")

// Escaping 闭包
var completionHandlers: [() -> Void] = []
func someFuncWithEscapingClosure(completionHandler: @escaping () -> Void) {
    completionHandlers.append(completionHandler)
}

func someFuncWithNoEscapingClosure(closure: () -> Void) {
    closure()
}

class SomeClass {
    var x = 10
    func doSomething() {
        someFuncWithEscapingClosure {
            self.x = 100 // 这里为什么用 self.x = 100 而不是用 x = 100
        }
        
        someFuncWithNoEscapingClosure {
            x = 200
        }
    }
}

let instance = SomeClass()
instance.doSomething()
print("instance.x = \(instance.x)")

completionHandlers.first?()
print("instance.x = \(instance.x)")

// Autoclosures
var numberInLine = ["one", "two", "three", "four", "five"]
print("count = \(numberInLine.count)")
let numberProvider = { numberInLine.remove(at: 0)}
print("count = \(numberInLine.count)")
print("count = \(numberInLine.count)")
print("count = \(numberInLine.count)")
print("count = \(numberInLine.count)")
print("count = \(numberInLine.count)")
print("Now serving \(numberProvider())")
print("count = \(numberInLine.count)")
print("numberInLine \(numberInLine)")

func serve(number numberProvider:() -> String) {
    print("Now serving \(numberProvider())!")
}

serve(number: { numberInLine.remove(at: 0) })
print("count = \(numberInLine.count)")
print("numberInLine \(numberInLine)")

func serve2(number numberProvider: @autoclosure () -> String) {
    print("Now serving \(numberProvider())!")
}
serve2(number: numberInLine.remove(at: 0))
print("count = \(numberInLine.count)")
print("numberInLine \(numberInLine)")

var numberProviders: [() -> String] = []
func collectNumberProvider(_ numberProvider: @autoclosure @escaping () -> String) {
    numberProviders.append(numberProvider)
}

collectNumberProvider(numberInLine.remove(at: 0))
collectNumberProvider(numberInLine.remove(at: 0))

print("Collected \(numberProviders.count) numbers")
for numberProvider in numberProviders {
    print("Now serving \(numberProvider())")
}












